package cn.quevo.beetlsql;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import cn.quevo.core.entity.DoEntity;
import cn.quevo.core.exception.CustomException;
import org.beetl.sql.core.BeetlSQLException;
import org.beetl.sql.core.SQLManager;
import org.beetl.sql.core.SQLReady;
import org.beetl.sql.core.db.KeyHolder;
import org.beetl.sql.ext.spring4.SqlManagerFactoryBean;
import org.postgresql.util.PSQLException;
import org.postgresql.util.PSQLState;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import cn.quevo.core.entity.PageResult;
import cn.quevo.core.entity.exp.MapPageResult;
import cn.quevo.core.entity.query.Query;
import cn.quevo.core.entity.query.QueryPage;
import cn.quevo.core.enums.ResultStatusEnum;
import cn.quevo.core.exception.CheckParamsException;
import cn.quevo.core.exception.PageParamsException;
import cn.quevo.core.lang.StringUtils;
import cn.quevo.core.util.PageUtils;
import cn.quevo.core.util.RecordMap;

/**
 * BeetlSQl 工厂类
 *
 * @author July july_sky@foxmail.com
 * @version 1.0
 * @date 2017年12月14日 下午11:24:58
 * @Copyright ©2015-2035 湘豫(北京)科技有限公司. All Rights Reserved.
 */
public class BeetlSQLFactory {

    private Logger log = LoggerFactory.getLogger(BeetlSQLFactory.class);

    private SQLManager sqlManager;

    /**
     * 构造函数，注入sqlManagerFactoryBean
     *
     * @param sqlManagerFactoryBean
     */
    public BeetlSQLFactory(SqlManagerFactoryBean sqlManagerFactoryBean) {
        try {
            sqlManager = sqlManagerFactoryBean == null ? null : sqlManagerFactoryBean.getObject();
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw new RuntimeException("SQLManager 未注入");
        }
    }

    /**
     * 对外暴露 SQLManager
     *
     * @return
     * @author July july_sky@foxmail.com
     * @date 2017年11月21日 下午11:09:05
     */
    public SQLManager getSqlManager() {
        return sqlManager;
    }

    /**
     * 列表查询
     *
     * @param sqlId
     * @param clazz  返回对象类型
     * @param params 查询参数
     * @return
     * @author July july_sky@foxmail.com
     * @date 2017年11月21日 下午11:09:19
     */
    public <T> List<T> selectList(String sqlId, Class<T> clazz, Object params) {
        try {
            return this.getSqlManager().select(sqlId, clazz, params);
        } catch (Exception e) {
            throw new CustomException(ResultStatusEnum.DB_SEARCH_ERROR, e);
        }
    }

    /**
     * 分页查询
     *
     * @param sqlId
     * @param clazz  返回对象类型
     * @param params 查询参数
     * @return
     * @author July july_sky@foxmail.com
     * @date 2017年11月21日 下午11:09:19
     */
    public <T> List<T> selectList(String sqlId, Class<T> clazz, Object params, Integer curPage, Integer pageSize) {
        try {
            return this.getSqlManager().select(sqlId, clazz, params, curPage, pageSize);
        } catch (Exception e) {
            throw new CustomException(ResultStatusEnum.DB_SEARCH_ERROR, e);
        }
    }

    /**
     * 格式化Map
     *
     * @param map
     * @return
     * @author July july_sky@foxmail.com
     * @date 2017年11月21日 下午11:09:34
     */
    @SuppressWarnings("rawtypes")
    protected Map<String, Object> formatMap(Map map) {
        Map<String, Object> _row = new HashMap<>();
        for (Object key : map.keySet()) {
            if (!StringUtils.isBlank(key)) {
                _row.put(key.toString(), map.get(key));
            }
        }
        return _row;
    }


    /**
     * 分页查询
     *
     * @param sqlId
     * @param params
     * @return
     * @throws CheckParamsException
     * @author July july_sky@foxmail.com
     * @date 2018年1月4日 下午10:27:03
     */
    @SuppressWarnings("rawtypes")
    private List<Map<String, Object>> selectList(String sqlId, Object params, Integer curPage, Integer pageSize) {
        List<Map> results = this.getSqlManager().select(sqlId, Map.class, params, curPage, pageSize);
        if (StringUtils.isEmpty(results)) {
            return null;
        }
        List<Map<String, Object>> list = new ArrayList<>();
        for (Map map : results) {
            list.add(formatMap(map));
        }
        return list;
    }


    /**
     * 列表查询,返回Map
     *
     * @param sqlId
     * @param params 查询参数
     * @return
     * @author July july_sky@foxmail.com
     * @date 2017年11月21日 下午11:09:41
     */
    @SuppressWarnings({"rawtypes"})
    public List<Map<String, Object>> selectList(String sqlId, Object params) {
        List<Map> results = this.getSqlManager().select(sqlId, Map.class, params);
        if (StringUtils.isEmpty(results)) {
            return null;
        }
        List<Map<String, Object>> list = new ArrayList<>();
        for (Map map : results) {
            list.add(formatMap(map));
        }
        return list;
    }

    /**
     * 查询结果集转换为Map
     *
     * @param sqlId
     * @param params
     * @return
     * @author July july_sky@foxmail.com
     * @date 2017年11月21日 下午11:11:31
     */
    public Map<String, Object> selectListToMap(String sqlId, Object params) {
        return selectListToMap(sqlId, "key", params);
    }

    /**
     * 查询结果集转换为Map
     *
     * @param sqlId  SQL ID
     * @param params 查询参数
     * @return 结果集
     */
    @SuppressWarnings("rawtypes")
    public Map<String, Object> selectListToMap(String sqlId, String key, Object params) {
        try {
            List<Map> results = this.getSqlManager().select(sqlId, Map.class, params);
            if (StringUtils.isEmpty(results)) {
                return null;
            }
            return RecordMap.convertRecordMap(results, key);
        } catch (Exception e) {
            throw new CustomException(ResultStatusEnum.DB_SEARCH_ERROR, e);
        }
    }

    /**
     * 列表查询
     *
     * @param sql   执行的SQL语句
     * @param clazz 返回类型
     * @param args  查询参数
     * @return
     * @author July july_sky@foxmail.com
     * @date 2017年11月21日 下午11:11:43
     */
    public <T> List<T> executeList(String sql, Class<T> clazz, Object... args) {
        try {
            return this.getSqlManager().execute(new SQLReady(sql, args), clazz);
        } catch (Exception e) {
            throw new CustomException(ResultStatusEnum.DB_SEARCH_ERROR, e);
        }
    }

    /**
     * 列表查询，返回Map
     *
     * @param sql  执行的SQL语句
     * @param args 查询参数
     * @return
     * @author July july_sky@foxmail.com
     * @date 2017年11月21日 下午11:11:57
     */
    @SuppressWarnings("rawtypes")
    public List<Map<String, Object>> executeList(String sql, Object... args) {
        try {
            List<Map> results = this.getSqlManager().execute(new SQLReady(sql, args), Map.class);
            if (StringUtils.isEmpty(results)) {
                return null;
            }
            List<Map<String, Object>> list = new ArrayList<>();
            for (Map map : results) {
                list.add(formatMap(map));
            }
            return list;
        } catch (Exception e) {
            throw new CustomException(ResultStatusEnum.DB_SEARCH_ERROR, e);
        }
    }

    /**
     * 列表查询并转换为Map
     *
     * @param sql  执行的SQL语句
     * @param args 查询参数
     * @return
     * @author July july_sky@foxmail.com
     * @date 2017年11月21日 下午11:12:27
     */
    public Map<String, Object> executeListToMap(String sql, Object... args) {
        return executeListToMap(sql, "key", "value", args);
    }

    /**
     * 列表查询并转换为Map
     *
     * @param sql   查询的SQL语句
     * @param key   作为Map的key的字段
     * @param args  查询条件
     * @return
     * @author July july_sky@foxmail.com
     * @date 2017年11月21日 下午11:12:51
     */
    @SuppressWarnings("rawtypes")
    public Map<String, Object> executeListToMap(String sql, String key, Object... args) {
        try {
            List<Map> results = this.getSqlManager().execute(new SQLReady(sql, args), Map.class);
            if (StringUtils.isEmpty(results)) {
                return null;
            }
            return RecordMap.convertRecordMap(results, key);
        } catch (Exception e) {
            throw new CustomException(ResultStatusEnum.DB_SEARCH_ERROR, e);
        }
    }

    /**
     * 获取单个对象
     *
     * @param sqlId
     * @param params
     * @param target
     * @return
     * @author July july_sky@foxmail.com
     * @date 2017年11月21日 下午11:13:24
     */
    public <T> T getObject(String sqlId, Object params, Class<T> target) {
        try {
            return this.getSqlManager().selectSingle(sqlId, params, target);
        } catch (Exception e) {
            throw new CustomException(ResultStatusEnum.DB_SEARCH_ERROR, e);
        }
    }

    /**
     * 获取单个对象
     *
     * @param sqlId
     * @param params
     * @return
     * @author July july_sky@foxmail.com
     * @date 2017年11月21日 下午11:13:37
     */
    @SuppressWarnings("unchecked")
    public Map<String, Object> getObject(String sqlId, Object params) {
        try {
            return this.getSqlManager().selectSingle(sqlId, params, Map.class);
        } catch (Exception e) {
            throw new CustomException(ResultStatusEnum.DB_SEARCH_ERROR, e);
        }
    }

    /**
     * 通过SQL查询记录集
     *
     * @param sql   执行的SQL语句
     * @param clazz 返回类型
     * @param args  查询参数
     * @return
     * @throws CheckParamsException
     * @author July july_sky@foxmail.com
     * @date 2017年11月21日 下午11:13:51
     */
    public <T> T executeObject(String sql, Class<T> clazz, Object... args) throws CheckParamsException {
        List<T> list = this.executeList(sql, clazz, args);
        if (StringUtils.isEmpty(list)) {
            return null;
        }
        if (list.size() > 1) {
            throw new CheckParamsException(ResultStatusEnum.RECORD_NOT_EXISTS);
        }
        return list.get(0);
    }

    /**
     * 更新
     *
     * @param sqlId
     * @param params
     * @return
     * @author July july_sky@foxmail.com
     * @date 2017年11月21日 下午11:16:19
     */
    public Integer updateObject(String sqlId, Object params) {
        try {
            return this.getSqlManager().update(sqlId, params);
        } catch (Exception e) {
            throw new CustomException(ResultStatusEnum.EDIT_FAILURE, e);
        }
    }

    /**
     * 新增
     *
     * @param sqlId
     * @param params
     * @return
     * @author July july_sky@foxmail.com
     * @date 2017年11月21日 下午11:16:27
     */
    public Integer insertObject(String sqlId, Object params) {
        try {
            return this.getSqlManager().insert(sqlId, params, null);
        } catch (Exception e) {
            throw new CustomException(ResultStatusEnum.SAVE_FAILURE, e);
        }
    }

    /**
     * 新增并返回自增主键ID
     *
     * @param sqlId
     * @param entity
     * @return
     * @author July july_sky@foxmail.com
     * @date 2017年11月21日 下午11:08:32
     */
    public Integer createObject(String sqlId, DoEntity entity) {
        return createObject(sqlId, entity, "id");
    }

    /**
     * 新增并返回自增主键ID
     *
     * @param sqlId
     * @param entity
     * @param keyName 主键字段名称
     * @return
     * @author July july_sky@foxmail.com
     * @date 2017年11月21日 下午11:18:36
     */
    public <T extends DoEntity> Integer createObject(String sqlId, T entity, String keyName) {
        KeyHolder keyHolder = new KeyHolder();
        try {
            int count = this.getSqlManager().insert(sqlId, entity, keyHolder, keyName);
            entity.setId(keyHolder.getLong());
            return count;
        } catch (BeetlSQLException e) {
            if (e.getCause() instanceof PSQLException) {
                PSQLException psqlException = (PSQLException) e.getCause();
                if (psqlException.getSQLState().equalsIgnoreCase(PSQLState.INVALID_CURSOR_STATE.getState())) {
                    return 0;
                }
            }
            throw new CustomException(ResultStatusEnum.SAVE_FAILURE, e);
        }
    }

    /**
     * 新增
     *
     * @param sqlId     新增的SQL
     * @param params    参数
     * @param keyHolder 自增
     * @return
     */
    public Integer insertObject(String sqlId, Object params, KeyHolder keyHolder) {
        try {
            return this.getSqlManager().insert(sqlId, params, keyHolder);
        } catch (BeetlSQLException e) {
            if (e.getCause() instanceof PSQLException) {
                PSQLException psqlException = (PSQLException) e.getCause();
                if (psqlException.getSQLState().equalsIgnoreCase(PSQLState.INVALID_CURSOR_STATE.getState())) {
                    return 0;
                }
            }
            throw new CustomException(ResultStatusEnum.SAVE_FAILURE, e);
        }
    }

    /**
     * 新增
     *
     * @param sqlId     新增的SQL
     * @param params    参数
     * @param keyHolder 自增指返回对象
     * @param keyName   自增字段名称
     * @return
     * @author July july_sky@foxmail.com
     * @date 2017年11月21日 下午11:18:36
     */
    public Integer insertObject(String sqlId, Object params, KeyHolder keyHolder, String keyName) {
        try {
            return this.getSqlManager().insert(sqlId, params, keyHolder, keyName);
        } catch (BeetlSQLException e) {
            if (e.getCause() instanceof PSQLException) {
                PSQLException psqlException = (PSQLException) e.getCause();
                if (psqlException.getSQLState().equalsIgnoreCase(PSQLState.INVALID_CURSOR_STATE.getState())) {
                    return 0;
                }
            }
            throw new CustomException(ResultStatusEnum.SAVE_FAILURE, e);
        }
    }


    /**
     * 分页获取记录集
     *
     * @param sqlId   列表查询SQLID
     * @param countId 总量查询SQLID
     * @param page    查询参数
     * @return
     * @author July july_sky@foxmail.com
     * @date 2017年11月21日 下午11:18:36
     */
    public <T> PageResult<T> getPageResult(final String sqlId, final String countId, QueryPage page, Class<T> target)
      throws PageParamsException {
        PageResult<T> result = new PageResult<>();
        // 如果设置不分页或者countId为空，则不分页
        if (!page.getIsPaging() || StringUtils.isBlank(countId)) {
            List<T> list = this.selectList(sqlId, target, page);
            if (StringUtils.isBlank(list)) {
                return result;
            }
            result.setTotal(Long.valueOf(list.size()));
            result.setCountPage(1);
            result.setFirst(1L);
            result.setLast(result.getTotal());
            result.setRows(list);
            return result;
        }
        long total = this.getObject(countId, page, Long.class);
        if (total <= 0) {
            return result;
        }
        result.setTotal(total);
        result.setPageSize(page.getPageSize());
        result.setCurPage(page.getCurPage());
        PageUtils.pagination(result);
        result.setRows(this.selectList(sqlId, target, page, result.getFirst().intValue(), result.getPageSize()));
        return result;
    }

    /**
     * 分页获取记录集
     *
     * @param SQL_PRE SQL前缀
     * @param sqlId   列表查询SQLID
     * @param countId 总量查询SQLID
     * @param page    查询参数
     * @return
     * @author July july_sky@foxmail.com
     * @date 2017年11月21日 下午11:18:36
     */
    public <T> PageResult<T> getPageResult(final String SQL_PRE, final String sqlId, final String countId,
      QueryPage page, Class<T> target) throws PageParamsException {
        PageResult<T> result = new PageResult<>();
        // 如果设置不分页或者countId为空，则不分页
        if (!page.getIsPaging() || StringUtils.isBlank(countId)) {
            List<T> list = this.selectList(SQL_PRE + sqlId, target, page);
            if (StringUtils.isBlank(list)) {
                return result;
            }
            result.setTotal(Long.valueOf(list.size()));
            result.setCountPage(1);
            result.setFirst(1L);
            result.setLast(result.getTotal());
            result.setRows(list);
            return result;
        }
        long total = this.getObject(SQL_PRE + countId, page, Long.class);
        if (total <= 0) {
            return result;
        }
        result.setTotal(total);
        result.setPageSize(page.getPageSize());
        result.setCurPage(page.getCurPage());
        PageUtils.pagination(result);
        result.setRows(this.selectList(SQL_PRE + sqlId, target, page, result.getFirst().intValue(), result.getPageSize()));
        return result;
    }

    /**
     * 分页获取记录集
     *
     * @param sqlId   列表查询SQLID
     * @param countId 总量查询SQLID
     * @param page    查询参数
     * @return
     * @author July july_sky@foxmail.com
     * @date 2017年11月21日 下午11:18:36
     */
    public MapPageResult getPageResult(final String sqlId, final String countId, QueryPage page)
      throws PageParamsException {
        MapPageResult result = new MapPageResult();
        // 如果设置不分页或者countId为空，则不分页
        if (!page.getIsPaging() || StringUtils.isBlank(countId)) {
            List<Map<String, Object>> list = this.selectList(sqlId, page);
            if (StringUtils.isBlank(list)) {
                return result;
            }
            result.setTotal(Long.valueOf(list.size()));
            result.setCountPage(1);
            result.setFirst(1L);
            result.setLast(result.getTotal());
            result.setRows(list);
            return result;
        }
        long total = this.getObject(countId, page, Long.class);
        if (total <= 0) {
            return result;
        }
        result.setTotal(total);
        result.setPageSize(page.getPageSize());
        result.setCurPage(page.getCurPage());
        PageUtils.pagination(result);
        result.setRows(this.selectList(sqlId, page, result.getFirst().intValue(), result.getPageSize()));
        return result;
    }

    /**
     * 分页获取记录集
     *
     * @param SQL_PRE SQL前缀
     * @param sqlId   列表查询SQLID
     * @param countId 总量查询SQLID
     * @param page    查询参数
     * @return
     * @author July july_sky@foxmail.com
     * @date 2017年11月21日 下午11:18:36
     */
    public MapPageResult getPageResult(final String SQL_PRE, final String sqlId, final String countId, QueryPage page)
      throws PageParamsException {
        MapPageResult result = new MapPageResult();
        // 如果设置不分页或者countId为空，则不分页
        if (!page.getIsPaging() || StringUtils.isBlank(countId)) {
            List<Map<String, Object>> list = this.selectList(SQL_PRE + sqlId, page);
            if (StringUtils.isBlank(list)) {
                return result;
            }
            result.setTotal(Long.valueOf(list.size()));
            result.setCountPage(1);
            result.setFirst(1L);
            result.setLast(result.getTotal());
            result.setRows(list);
            return result;
        }
        long total = this.getObject(SQL_PRE + countId, page, Long.class);
        if (total <= 0) {
            return result;
        }
        result.setPageSize(page.getPageSize());
        result.setCurPage(page.getCurPage());
        result.setTotal(total);
        PageUtils.pagination(result);
        result.setRows(this.selectList(SQL_PRE + sqlId, page, result.getFirst().intValue(), result.getPageSize()));
        return result;
    }

}
